#include "serialform.h"
#include "ui_serialform.h"
#include "serialthread.h"

#include <QScreen>
#include <QDebug>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QFile>
#include <QFileDialog>

#include <QDateTime>

#define TIMEMS          qPrintable(QTime::currentTime().toString("HH:mm:ss zzz"))
#define TIME            qPrintable(QTime::currentTime().toString("HH:mm:ss"))
#define QDATE           qPrintable(QDate::currentDate().toString("yyyy-MM-dd"))
#define QTIME           qPrintable(QTime::currentTime().toString("HH-mm-ss"))
#define DATETIME        qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"))
#define STRDATETIME     qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss"))
#define STRDATETIMEMS   qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz"))

#define MyDATETIMEMS    qPrintable(QDateTime::currentDateTime().toString("MM-dd HH:mm:ss zzz"))

SerialForm::SerialForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SerialForm)
{
    ui->setupUi(this);

    connect(this, &SerialForm::SignalSerialOpen,
            SerialThread::Instance(), &SerialThread::SlotSerialOpen, Qt::BlockingQueuedConnection);
    connect(this, &SerialForm::SignalSerialClose,
            SerialThread::Instance(), &SerialThread::SlotSerialClose, Qt::BlockingQueuedConnection);
    connect(this, &SerialForm::SignalUpdate,
            SerialThread::Instance(), &SerialThread::SlotUpdateFireware);
    connect(this, &SerialForm::SignalUpdateStop,
            SerialThread::Instance(), &SerialThread::SlotUpdateStop);
    connect(this, &SerialForm::SignalReset,
            SerialThread::Instance(), &SerialThread::SlotReset);

    connect(SerialThread::Instance(), &SerialThread::SignalUpdateTips,
            this, &SerialForm::SlotUpdateTips);
    connect(SerialThread::Instance(), &SerialThread::SignalSerialLog,
            this, &SerialForm::SlotRecvSerialLog);
    connect(SerialThread::Instance(), &SerialThread::SignalUpdateProgress,
            this, &SerialForm::SlotUpdateProgress);

    ui->updateProgressBar->setAlignment(Qt::AlignHCenter);
    ui->updateProgressBar->setValue(0);

    ui->serialBaudBox->addItem("115200");
    ui->serialBaudBox->addItem("19200");
    ui->serialBaudBox->addItem("9600");

    RefreshSerialPort();

    //QScreen *screen=QGuiApplication::primaryScreen();
    //QRect mm=screen->availableGeometry() ;
    //this->resize(mm.width()/2 - 20, mm.height() * 3 / 4 - 20);
}

SerialForm::~SerialForm()
{
    delete ui;
}

void SerialForm::RefreshSerialPort()
{
    ui->serialComboBox->clear();
    for(auto &dev:QSerialPortInfo::availablePorts())
    {
        QSerialPort com;

        com.setPort(dev);
        if(com.open(QIODevice::ReadWrite)) {
            ui->serialComboBox->addItem(com.portName());
            com.close();
        }
    }
}


void SerialForm::on_serialOperationBtn_clicked()
{
    if(m_SerialIsOpen) {
        ui->serialComboBox->setEnabled(true);
        ui->refreshSerialButton->setEnabled(true);
        emit SignalSerialClose();

        m_SerialIsOpen = false;
        ui->serialOperationBtn->setText(u8"打开串口");
    }
    else {
        QString baud = ui->serialBaudBox->currentText();
        emit SignalSerialOpen(ui->serialComboBox->currentText(), baud.toUInt());

        //QThread::msleep(200);
        m_SerialIsOpen = SerialThread::Instance()->IsOpen();
        if(m_SerialIsOpen) {
            ui->serialOperationBtn->setText(u8"关闭串口");

            ui->serialComboBox->setEnabled(false);
            ui->refreshSerialButton->setEnabled(false);
        }
    }
}

void SerialForm::on_refreshSerialButton_clicked()
{
    if(m_SerialIsOpen) return;

    emit SignalSerialClose();
    //QThread::msleep(100);

    if(!SerialThread::Instance()->IsOpen()) {
        ui->serialComboBox->clear();
        m_SerialIsOpen = false;
        ui->serialOperationBtn->setText(u8"打开串口");
        RefreshSerialPort();
    }
}

void SerialForm::on_fireFileOpenBtn_clicked()
{
    QString path = QFileDialog::getOpenFileName(nullptr,
                                                u8"升级固件",
                                                "/",
                                                "Bin File(*.bin)");
    if(path.isEmpty()) return;

    ui->fireFileEdit->setText(path);
}

void SerialForm::on_startUpdateBtn_clicked()
{
    QString path = ui->fireFileEdit->text();
    if(path.isEmpty() || !m_SerialIsOpen) return;

    if(SerialThread::Instance()->IsUpdate()) {
        QMessageBox::information(0, "warning", u8"当前升级未完成");
        return;
    }

    QFile file(path);
    if(file.open(QIODevice::ReadOnly)) {
        QDataStream out(&file);

        QByteArray totalFile;
        char tmpBuf[1024];
        while(!out.atEnd())
        {
            int sz = out.readRawData(tmpBuf, 1024);
            QByteArray block(tmpBuf, sz);
            totalFile += block;
        }

        int fileLen = totalFile.length();
        qDebug() << "read fireware length " << fileLen;

        if(fileLen > 100 && fileLen <= 15104) {
            int updateLen = fileLen / 32 + ((fileLen % 32) > 0);

            ui->updateProgressBar->setRange(0, updateLen+1);
            ui->serialOperationBtn->setEnabled(false);
            ui->startUpdateBtn->setEnabled(false);
            ui->fireFileOpenBtn->setEnabled(false);
            emit SignalUpdate(totalFile);
        }
        else {
            QMessageBox::information(nullptr, "warning", u8"固件长度范围(0.1~14.75KByte)");
        }
    }
}

void SerialForm::SlotUpdateProgress(int index)
{
    ui->updateProgressBar->setValue(index);
}

void SerialForm::SlotSerialError()
{
    ui->updateProgressBar->setValue(0);
    if(m_SerialIsOpen) {
        ui->serialComboBox->setEnabled(true);
        ui->refreshSerialButton->setEnabled(true);
        ui->serialOperationBtn->setEnabled(true);
        ui->startUpdateBtn->setEnabled(true);
        ui->fireFileOpenBtn->setEnabled(true);
        m_SerialIsOpen = false;
        ui->serialOperationBtn->setText(u8"打开串口");
    }
}

void SerialForm::SlotUpdateTips(const QString &tip, quint8 flag)
{
    if(flag != 0) ui->updateProgressBar->setValue(0);
    ui->serialOperationBtn->setEnabled(true);
    ui->startUpdateBtn->setEnabled(true);
    ui->fireFileOpenBtn->setEnabled(true);
    QMessageBox::information(nullptr, "update tips", tip);

    SlotRecvSerialLog(tip);
}

void SerialForm::SlotRecvSerialLog(const QString& data)
{
    QString msg = QString("[%1]%2").arg(MyDATETIMEMS).arg(data);
    ui->serialPlainEdit->appendPlainText(msg);

    if(ui->serialPlainEdit->blockCount() > 10000) {
        ui->serialPlainEdit->clear();
    }
}

void SerialForm::on_clearLogBtn_clicked()
{
    ui->serialPlainEdit->clear();
}

void SerialForm::on_updateStopBtn_clicked()
{
    ui->updateProgressBar->setValue(0);
    ui->serialOperationBtn->setEnabled(true);
    ui->startUpdateBtn->setEnabled(true);
    ui->fireFileOpenBtn->setEnabled(true);

    emit SignalUpdateStop();
}

void SerialForm::on_resetBtn_clicked()
{
    emit SignalReset();
}

int SerialForm::hexStrToChar(char ch)
{
    if(ch >= '0' && ch <= '9') {
        return ch - '0';
    }

    if(ch >= 'a' && ch <= 'f') {
        return ch - 'a' + 10;
    }

    if(ch >= 'A' && ch <= 'F') {
        return ch - 'A' + 10;
    }

    return 0xff;
}

QString SerialForm::byteArrayToHexStr(const QByteArray &data)
{
    QString temp = "";
    QString hex = data.toHex();

    for (int i = 0; i < hex.length(); i = i + 2) {
        temp += hex.mid(i, 2) + " ";
    }

    return temp.trimmed().toUpper();
}

QByteArray SerialForm::hexStrToByteArray(const QString& data)
{
    QByteArray tmp;

    int len = data.length(), i, j;
    tmp.resize(len/2);

    int hhex, lhex;
    for(i = 0, j = 0; i < len;) {
        char hch = data.at(i).toLatin1();
        if(hch == ' ' ) {
            i++;
            continue;
        }
        i++;
        if(i >= len) break;
        hhex = hexStrToChar(hch);

        char lch = data.at(i).toLatin1();
        lhex = hexStrToChar(lch);

        i++;

        //左字符出错 || 右字符出错且不是空格跳过
        if(hhex == 0xff) continue;
        if(lhex == 0xff && lch != ' ') continue;
        //右字符是空格，则左字符按单字符补0处理
        if(lhex != 0xff) {
            hhex = hhex * 16 + lhex;
        }
        tmp[j] = static_cast<char>(hhex);
        j++;
    }

    if(j < len/2) {
        tmp.remove(j, len/2 - j);
    }

    return tmp;
}

void SerialForm::on_crcCheckBtn_clicked()
{
    QString input = ui->crcValEdit->text();
    input.replace(" ", "");

    int len = input.length();
    if(len > 0) {
        int i = 0, j = 0;
        for(i = 0; i < len; i++)
        {
            if((input.at(i) >= '0' && input.at(i) <= '9')
            || (input.at(i) >= 'A' && input.at(i) <= 'F')
            || (input.at(i) >= 'a' && input.at(i) <= 'f')) {
                j++;
            }
        }
        if(i != j) {
            QMessageBox::information(nullptr, "warning", "invalid hex string!");
        }
        else {
            QByteArray data2 = hexStrToByteArray(input);
            quint16 crcval = SerialThread::Instance()-> \
                    inverse_crc_16(reinterpret_cast<uint8_t*>(data2.data()), data2.length());
            data2.push_back(static_cast<char>(crcval >> 8));
            data2.push_back(static_cast<char>(crcval&0xff));

            QString tmp = byteArrayToHexStr(data2);
            ui->crcValEdit->setText(tmp);
        }
    }
}
